﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace DemoP
{
    using Microsoft.Research.Kinect;
    using Microsoft.Research.Kinect.Nui;
    using Microsoft.Research.Kinect.Audio;
    using Microsoft.Win32;
    using System.IO;
    using System.Threading;

    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        private Runtime nuiRuntime;
        private KinectAudioSource audioSource;

        public MainWindow()
        {
            InitializeComponent();
            this.soundPOn.MediaEnded += new RoutedEventHandler(soundPOn_MediaEnded);
            nuiRuntime = new Runtime();
            nuiRuntime.Initialize(RuntimeOptions.UseColor | RuntimeOptions.UseSkeletalTracking | RuntimeOptions.UseDepthAndPlayerIndex);
            nuiRuntime.VideoStream.Open(ImageStreamType.Video, 2, ImageResolution.Resolution640x480, ImageType.Color);

            nuiRuntime.VideoFrameReady += new EventHandler<ImageFrameReadyEventArgs>(nuiRuntime_VideoFrameReady);
            nuiRuntime.SkeletonFrameReady += new EventHandler<SkeletonFrameReadyEventArgs>(nuiRuntime_SkeletonFrameReady);
        }

        void soundPOn_MediaEnded(object sender, RoutedEventArgs e)
        {
            isPOnPlaying = false;
        }

        private bool isPOnPlaying = false;
        void nuiRuntime_SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)
        {
            var skeltonFrame = e.SkeletonFrame;
            foreach (var data in skeltonFrame.Skeletons)
            {
                if (data.TrackingState == SkeletonTrackingState.Tracked)
                {
                    double handX = data.Joints[JointID.HandRight].Position.X;
                    double handY = data.Joints[JointID.HandRight].Position.Y;

                    double headX = data.Joints[JointID.Head].Position.X;
                    double headY = data.Joints[JointID.Head].Position.Y;

                    double wHandX = 320 + 320 * handX;
                    double wHandY = 240 - 240 * handY;

                    double wHeadX = 320 + 320 * headX;
                    double wHeadY = 240 - 240 * headY;

                    double r = System.Math.Sqrt((handX - headX) * (handX - headX) + (handY - headY) * (handY - headY));
                    tbDistance.Text = String.Format("Distance={0:0.00}",r);
                    if (r < 0.2)
                    {
                        // P!
                        tbPStatus.Text = "P!";
                        if (!isPOnPlaying)
                        {
                            this.soundPOn.Position = TimeSpan.FromMilliseconds(0);
                            this.soundPOn.Play();
                            isPOnPlaying = true;
                        }
                    }
                    else
                    {
                        tbPStatus.Text = "";
                        if (isPOnPlaying)
                        {
                            this.soundPOn.Stop();
                            isPOnPlaying = false;
                        }
                    }
                    break;
                }
            }
        }

        void nuiRuntime_VideoFrameReady(object sender, ImageFrameReadyEventArgs e)
        {
            PlanarImage image = e.ImageFrame.Image;
            this.videoFrame.Source = BitmapImage.Create(image.Width, image.Height
                , 96, 96, PixelFormats.Bgr32, null, image.Bits, image.Width * image.BytesPerPixel);
        }

        private void buttonOpenFile_Click(object sender, RoutedEventArgs e)
        {
            OpenFileDialog dialog = new OpenFileDialog();
            Nullable<bool> result = dialog.ShowDialog();
            if (result == true)
            {
                tbFileName.Text = dialog.FileName;
            }
        }

        private void buttonRecordAudio_Click(object sender, RoutedEventArgs e)
        {
            if (buttonRecordAudio.Content.ToString().CompareTo("Record") == 0)
            {
                buttonRecordAudio.Content = "End";
                audioSource = new KinectAudioSource();
                audioSource.SystemMode = SystemMode.OptibeamArrayOnly;
                recordingThread = new Thread(MainWindow.RecodingThreadFunc);
                recording = true;
                recordingThread.Start(this);
            }
            else
            {
                if (audioSource != null)
                {
                    audioSource.Dispose();
                    buttonRecordAudio.Content = "Record";
                    lock (this)
                    {
                        recording = false;
                        recordingThread.Join();
                    }
                }
            }
        }
        private bool recording = false;
        private Thread recordingThread;

        private static void RecodingThreadFunc(object target)
        {
            MainWindow my = target as MainWindow;
            my.RecordingWork();
        }

        private void RecordingWork()
        {
            var buffer = new byte[4096];
            const int recordingTime = 300;
            const int recordingLength = recordingTime * 2 * 16000;
            int totalCount=0;
            string tmpFileName = tbFileName.Text + ".tmp";
            using (var tmpStream = new FileStream(tmpFileName, FileMode.Create))
            {
                using (var audioStream = audioSource.Start())
                {
                    int count= 0;
                    while ((count = audioStream.Read(buffer, 0, buffer.Length)) > 0 && totalCount < recordingLength)
                    {
                        tmpStream.Write(buffer, 0, count);
                        totalCount += count;

                        lock (this)
                        {
                            if (!recording)
                            {
                                audioSource.Stop();
                                break;
                            }
                        }
                    }
                }
            }
            using (var fileStream = new FileStream(tbFileName.Text, FileMode.Create))
            {
                WriteWaveHeader(fileStream, totalCount);
                using (var tmpStream = new FileStream(tmpFileName, FileMode.Open))
                {
                    int count = 0;
                    while ((count < totalCount))
                    {
                        int size= tmpStream.Read(buffer, 0, buffer.Length);
                        fileStream.Write(buffer, 0, size);
                        count += size;
                    }
                }
            }
            
        }

        private void WriteWaveHeader(Stream stream, int dataLength)
        {
            using (MemoryStream memStream = new MemoryStream(64))
            {
                int cbFormat = 18; //sizeof(WAVEFORMATEX)
                WAVEFORMATEX format = new WAVEFORMATEX()
                {
                    wFormatTag = 1,
                    nChannels = 1,
                    nSamplesPerSec = 16000,
                    nAvgBytesPerSec = 32000,
                    nBlockAlign = 2,
                    wBitsPerSample = 16,
                    cbSize = 0
                };

                using (var bw = new BinaryWriter(memStream))
                {
                    //RIFF header
                    WriteString(memStream, "RIFF");
                    bw.Write(dataLength + cbFormat + 4); //File size - 8
                    WriteString(memStream, "WAVE");
                    WriteString(memStream, "fmt ");
                    bw.Write(cbFormat);

                    //WAVEFORMATEX
                    bw.Write(format.wFormatTag);
                    bw.Write(format.nChannels);
                    bw.Write(format.nSamplesPerSec);
                    bw.Write(format.nAvgBytesPerSec);
                    bw.Write(format.nBlockAlign);
                    bw.Write(format.wBitsPerSample);
                    bw.Write(format.cbSize);

                    //data header
                    WriteString(memStream, "data");
                    bw.Write(dataLength);
                    memStream.WriteTo(stream);
                }
            }                      
        }

        static void WriteString(Stream stream, string s)
        {
            byte[] bytes = Encoding.ASCII.GetBytes(s);
            System.Diagnostics.Debug.Assert(bytes.Length == s.Length);
            stream.Write(bytes, 0, bytes.Length);
        }


        struct WAVEFORMATEX
        {
            public ushort wFormatTag;
            public ushort nChannels;
            public uint nSamplesPerSec;
            public uint nAvgBytesPerSec;
            public ushort nBlockAlign;
            public ushort wBitsPerSample;
            public ushort cbSize;
        }
    }
}
